package com.geekhalo.schedule.v2;

import com.geekhalo.schedule.task.TaskConfig;
import com.geekhalo.schedule.task.TaskService;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.*;

@Service
public class LoadAndAyncRunScheduleService {
    @Autowired
    private TaskService taskService;

    @Autowired
    private TaskDefinitionV2Repository definitionV2Repository;

    // 线程池注册器，每个任务一个线程
    private Map<Long, ExecutorService> executorServiceRegistry = new HashMap<>();

    public LoadAndAyncRunScheduleService(){
    }

    @Scheduled(fixedDelay = 1000, initialDelay = 1000)
    public void loadAndRunTask(){
        Date now = new Date();
        // 加载所有待运行的任务
        // 1. 状态为 ENABLE
        // 2. 下一次运行时间小于 当前时间
        List<TaskDefinitionV2> shouldRunTasks = loadShouldRunTasks(now);
        // 遍历待运行任务
        for (TaskDefinitionV2 task : shouldRunTasks){
            // 1. 根据 Task Id 获取任务对应的线程池
            // 2. 将任务提交至线程池中
            this.executorServiceForTask(task.getId())
                    .submit(new TaskRunner(task.getId()));
        }
    }

    private List<TaskDefinitionV2> loadShouldRunTasks(Date now) {
        return this.definitionV2Repository.getByStatusAndNextRunAtLessThan(TaskStatus.ENABLE, now);
    }

    private void updateNextRunTime(TaskDefinitionV2 task, Date now) {
        task.updateNextRunTime(now);
        this.definitionV2Repository.save(task);
    }

    private void runTask(TaskDefinitionV2 task) {
        TaskConfig taskConfig = TaskConfig.builder()
                .name(task.getName())
                .data(task.getData())
                .build();
        this.taskService.runTask(taskConfig);
    }

    private ExecutorService executorServiceForTask(Long taskId){
        return this.executorServiceRegistry.computeIfAbsent(taskId, id->{
            BasicThreadFactory threadFactory = new BasicThreadFactory.Builder()
                    // 指定线程池名称
                    .namingPattern("Async-Task-"+ taskId +"-Thread-%d")
                    // 设置线程为 后台线程
                    .daemon(true)
                    .build();
            // 线程池核心配置：
            // 1. 每个线程池最多只有一个线程
            // 2. 线程空闲超过 10秒 进行自动回收
            // 3. 直接使用交互器，线程空闲进行任务交互
            // 4. 使用指定的线程工厂，设置线性名称
            // 5. 线程池饱和，自动丢弃最老的任务
            return new ThreadPoolExecutor(0, 1,
                    10L, TimeUnit.SECONDS,
                    new SynchronousQueue<>(),
                    threadFactory,
                    new ThreadPoolExecutor.DiscardOldestPolicy()
            );
        });
    }

    private class TaskRunner implements Runnable {
        private final Date now = new Date();
        private final Long taskId;
        public TaskRunner(Long taskId) {
            this.taskId = taskId;
        }

        @Override
        public void run() {
            // 重新加载任务，保持最新的任务状态
            TaskDefinitionV2 task = definitionV2Repository.findById(this.taskId).orElse(null);
            if (task != null && task.shouldRun(now)){
                // 运行任务
                runTask(task);

                // 更新任务的下一次运行时间
                updateNextRunTime(task, now);
            }
        }
    }
}
